using System;
using System.Collections.Generic;
using System.Collections;
using System.Text;
using System.Drawing;
using System.Xml;

namespace Athena.Core
{
    /// <summary>
    /// The Module is a set of Items that represents a biological system
    /// Modules have a list of parameters that are used in the reaction rates (Edge.Rate)
    /// contained inside the module. 
    /// Waste and Source are two global species that are included in all Modules. 
    /// </summary>
    [Serializable()]
    public class Module : Container
    {
        //public static Hashtable GlobalIDS = new Hashtable();        
        static GlobalSpecies EMPTY = new GlobalSpecies("empty", "EMPTY", 0);

        public static GlobalSpecies Empty
        {
            get
            {
                /*if (EMPTY.Drawable == null)
                {
                    EMPTY.GenerateDrawable(new PointF(-100, -100));
                    
                    if (EMPTY.Drawable is Objects.SpeciesGlyph)
                    {
                        ((Objects.SpeciesGlyph)EMPTY.Drawable).Shape = Objects.SpeciesGlyph.SHAPE.Empty;
                        ((Objects.SpeciesGlyph)EMPTY.Drawable).Dimensions = new SizeF(20, 20);
                        ((Objects.SpeciesGlyph)EMPTY.Drawable).ShowLabel = false;
                        ((Objects.SpeciesGlyph)EMPTY.Drawable).EdgeColor = Color.Gray;
                        ((Objects.SpeciesGlyph)EMPTY.Drawable).FillColorEnd = Color.LightGray;
                        ((Objects.SpeciesGlyph)EMPTY.Drawable).FillColorStart = Color.White;
                    }
                }*/
                return Module.EMPTY;
            }
        }

        public static int COUNT = 0;
        protected bool NOUPDATES = false;

        public static void ClearGlobals()
        {
            //Model.GlobalIDS.Clear();
            if (Empty.Drawable != null)
            {
                Empty.Drawable.RemoveAllAlias();
                Empty.Drawable.Remove();
                Empty.Drawable = null;
            }
            COUNT = 0;
            Core.Species.COUNT = 0;
            Part.COUNT = 0;
            Edge.COUNT = 0;
        }

        /// <summary>
        /// Created degradation drawables for all the degradation reactions in the module
        /// </summary>
        public virtual void GenerateDegradationDrawables()
        {
            if (Drawable == null) return;
            foreach (Item i in _Items.Values)
            {
                if (i is Node && i.Drawable != null)
                {
                    List<Edge> outedges = ((Node)i).OutgoingEdges;
                    foreach (Edge e in outedges)
                    {
                        Node[] outputs = e.Products;
                        if (outputs != null && outputs.Length == 1 && outputs[0] == Empty)
                        {
                            if (e.Drawable != null && e.Drawable is Objects.Connector && Empty.Drawable != null)
                            {
                                Objects.IDrawable wasteAlias = Empty.Drawable.GenerateAlias();
                                wasteAlias.Location = new PointF(i.Drawable.Bounds.Right + 10, (i.Drawable.Bounds.Top + i.Drawable.Bounds.Bottom) / 2);
                                ((Objects.Connector)e.Drawable).SubstituteNode(Empty.Drawable, wasteAlias);
                                if (Drawable is Objects.ContainerGlyph)
                                {
                                    wasteAlias.Parent = Drawable;
                                    //((Objects.Container)Drawable).Drawables.Add(wasteAlias);
                                    ((Objects.Connector)e.Drawable).Parent = Drawable;
                                    ((Objects.Connector)e.Drawable).MakePretty();
                                }
                            }
                        }
                    }

                    List<Edge> inedges = ((Node)i).IncomingEdges;
                    foreach (Edge e in inedges)
                    {
                        Node[] inputs = e.Reactants;
                        if (inputs != null && inputs.Length == 1 && inputs[0] == Empty)
                        {
                            if (e.Drawable != null && e.Drawable is Objects.Connector && Empty.Drawable != null)
                            {
                                Objects.IDrawable sourceAlias = Empty.Drawable.GenerateAlias();
                                sourceAlias.Location = new PointF(i.Drawable.Bounds.Right + 10, (i.Drawable.Bounds.Top + i.Drawable.Bounds.Bottom) / 2);
                                ((Objects.Connector)e.Drawable).SubstituteNode(Empty.Drawable, sourceAlias);
                                if (Drawable is Objects.ContainerGlyph)
                                {
                                    sourceAlias.Parent = Drawable;
                                    //((Objects.Container)Drawable).Drawables.Add(wasteAlias);
                                    ((Objects.Connector)e.Drawable).Parent = Drawable;
                                    ((Objects.Connector)e.Drawable).MakePretty();
                                }
                            }
                        }
                    }
                }                
            }
        }

        /// <summary>
        /// Generate the IDrawable for this module and store it in member Drawable
        /// </summary>
        /// <param name="point"></param>
        public override void GenerateDrawable(System.Drawing.PointF point)
        {
            Objects.ModuleGlyph glyph = new Objects.ModuleGlyph(point);
            glyph.Item = this;

            List<Node> nodes = LocalNodes;
            List<Edge> edges = LocalReactions;

            int max = nodes.Count * 30;
            Random rand = new Random();

            foreach (Item i in nodes)
            {
                if (i.Drawable == null) i.GenerateDrawable(new PointF(glyph.Location.X + rand.Next(max), glyph.Location.Y + rand.Next(max)));
                           
            }
            foreach (Item i in edges)
            {
                if (i.Drawable == null) i.GenerateDrawable(new PointF(glyph.Location.X + rand.Next(max), glyph.Location.Y + rand.Next(max)));
                    
            }

            _Drawable = glyph;

            ((Objects.ModuleGlyph)_Drawable).GenerateDrawablesFromItem();

            glyph.AdjustSize();
        }

        /// <summary>
        /// makes a clone of this module
        /// </summary>
        /// <returns></returns>
        public override object Clone()
        {
            return new Module(this);
        }

        /// <summary>
        /// The copy constructor
        /// </summary>
        /// <param name="copy">the module to copy</param>
        public Module(Module copy)
            : base(copy)
        {
            _Parameters = new Hashtable();
            foreach (string s in copy._Parameters)
                _Parameters.Add(s, (double)copy._Parameters[s]);            

            ++COUNT;
        }

        /// <summary>
        /// Default constructor
        /// </summary>
        public Module()
            : base("module", "M" + COUNT)
        {
            ++COUNT;
        }

        /// <summary>
        /// The main constructor
        /// </summary>
        /// <param name="type">type of this module</param>
        /// <param name="id">id</param>
        /// <param name="inputs">number of inputs</param>
        /// <param name="outputs">number of outputs</param>
        /// <param name="items">number of items contained, including inputs and outputs</param>
        public Module(string type, string id)
            : base(type, id)
        {
            ++COUNT;
        }
        /*
        /// <summary>
        /// All the input items
        /// </summary>
        public List<Item> Inputs
        {
            get
            {
                List<Item> list = new List<Item>();
                foreach (Item n in _Items.Values)
                {
                    if (n is Node)
                    {
                        List<Edge> edges = ((Node)n).IncomingEdges;
                        foreach (Edge e in edges)
                        {
                            if (!_Items.Contains(e.ID))
                            {
                                list.Add(n);
                                break;
                            }
                        }
                    }
                }
                return list;
            }
        }
        */
        /// <summary>
        /// Indicates whether or not this module contains DNA parts
        /// </summary>
        public virtual bool ContainsParts
        {
            get { return ContainsType(typeof(Part)); }
        }

        /// <summary>
        /// find an item or parameter
        /// </summary>
        /// <param name="sId"></param>
        /// <returns></returns>
        public override object this[string sId]
        {
            get
            {
                Hashtable parms = GlobalParameters;
                if (parms.ContainsKey(sId))
                {
                    Parameter p = parms[sId] as Parameter;
                    if (p != null)
                        return p.Value;
                }
                
                return base[sId];
            }
            set
            {

                if (value is double || value is int || value is float)
                {
                    Hashtable parms = GlobalParameters;
                    if (parms.ContainsKey(sId))
                    {
                        Parameter p = parms[sId] as Parameter;
                        if (p != null)
                        {
                            double d = 0;
                            if (value is double) d = (double)value;
                            if (value is int) d = (int)value;
                            if (value is float) d = (float)value;
                            p.Value = d;
                            return;
                        }
                    }
                }
                base[sId] = value;
            }
        }

        /// <summary>
        /// Automatically add the degrdation reaction for all species in list
        /// </summary>
        /// <param name="s"></param>
        public virtual void Degradation(string coeff, params Species[] slist)
        {
            foreach (Species s in slist)
            {
                Edge e = new Edge(s, Empty);
                e.Rate = (coeff + "*" + s.ID);
                e.SetParameter(coeff, 0.1);
                AddItem(e);
            }
        }
        /*
        /// <summary>
        /// returns the left-most part in this module. Null if there are no parts
        /// </summary>
        /// <returns></returns>
        public virtual Part GetLeftMostPart()
        {
            Hashtable parts = AllParts;

            foreach (Part p in parts.Values)
            {
                if (p.Left == null || !parts.ContainsValue(p.Left))
                {
                    return p;
                }
            }
            return null;
        }
        /// <summary>
        /// returns the right-most part in this module. Null if there are no parts
        /// </summary>
        /// <returns></returns>
        public virtual Part GetRightMostPart()
        {
            Hashtable parts = AllParts;
            foreach (Part p in parts.Values)
            {
                if (p.Right == null || !parts.ContainsValue(p.Right))
                {
                    return p;
                }
            }
            return null;
        }
        
        /// <summary>
        /// Gets and Sets the part to the left of this module
        /// </summary>
        /// <returns></returns>
        public virtual Item Left
        {
            get
            {
                Part l = GetLeftMostPart();
                if (l == null || l.Left == null) return null;
                if (l.Left.Parent is Module && l.Left.Parent != Parent) return l.Left.Parent;
                return l.Left;
            }
            set
            {
                Part l = GetLeftMostPart();
                if (l == null) return;
                if (value is Part)
                    l.Left = (value as Part);
                if (value is Module)
                    l.Left = (((Module)value).GetRightMostPart());
                if (value == null)
                    l.Left = null;
            }
        }
        /// <summary>
        /// Gets and Sets the part to the right of this module
        /// </summary>
        /// <returns></returns>
        public virtual Item Right
        {
            get
            {
                Part l = GetRightMostPart();
                if (l == null || l.Right == null) return null;
                if (l.Right.Parent is Module && l.Right.Parent != Parent) return l.Right.Parent;
                return l.Right;
            }
            set
            {
                Part l = GetRightMostPart();
                if (l == null) return;
                if (value is Part)
                    l.Right = (value as Part);
                if (value is Module)
                    l.Right = (((Module)value).GetLeftMostPart());
                if (value == null)
                    l.Right = null;
            }
        }
        */
        /*
        /// <summary>
        /// All the output items
        /// </summary>
        public virtual List<Item> Outputs
        {
            get
            {
                List<Item> list = new List<Item>();
                foreach (Item n in _Items.Values)
                {
                    if (n is Node)
                    {
                        List<Edge> edges = ((Node)n).OutgoingEdges;
                        foreach (Edge e in edges)
                        {
                            if (!_Items.Contains(e.ID))
                            {
                                list.Add(n);
                                break;
                            }
                        }
                    }
                }
                return list;
            }
        }
        */
        /// <summary>
        /// Mapping input/output items to other items
        /// </summary>
        protected Hashtable _MapTo = new Hashtable(),
                            _MapFrom = new Hashtable();

        /// <summary>
        /// Get the item that this exposed item is mapped to
        /// </summary>
        /// <param name="item">the item inside this module</param>
        /// <returns>the target item that this item is mapped to</returns>
        public virtual Item GetMappingTo(Item item)
        {
            if (_MapTo == null) _MapTo = new Hashtable();
            if (item == null || !IsParentOf(item)) return null;

            Module module = item.ParentModule;

            if (module == null) return null;

            if (module != this) return module.GetMappingTo(item);

            if (_MapTo.ContainsKey(item))
            {
                Item target = _MapTo[item] as Item;
                if (target == null || target.ParentModule == null)
                {
                    _MapTo.Remove(item);
                    return null;
                }
                else
                    return target;
            }

            return null;
        }

        /// <summary>
        /// Get the item that this exposed item is mapped from
        /// </summary>
        /// <param name="item">the item inside this module</param>
        /// <returns>the target item that this item is mapped to</returns>
        public virtual Item GetMappingFrom(Item item)
        {
            if (_MapFrom == null) _MapFrom = new Hashtable();
            if (item == null || !IsParentOf(item)) return null;

            Module module = item.ParentModule;

            if (module == null) return null;

            if (module != this) return module.GetMappingFrom(item);

            if (_MapFrom.ContainsKey(item))
            {
                Item target = _MapFrom[item] as Item;
                if (target == null || target.ParentModule == null)
                {
                    _MapFrom.Remove(item);
                    return null;
                }
                else
                    return target;
            }

            return null;
        }

        /// <summary>
        /// Maps the exposed item to another item outside the module
        /// </summary>
        /// <param name="item">item inside this module</param>
        /// <param name="target">target item outside the module</param>
        public virtual void SetMapping(Item item, Item target)
        {
            if (!(item is Node && (target is Node || target == null))) return;

            if (_MapTo == null) _MapTo = new Hashtable();

            if (item == null) return;

            Module module = item.ParentModule;

            if (module == null) return;

            if (module != this)
            {
                module.SetMapping(item,target);
                return;
            }

            if (target == null || target.ParentModule == null)
            {
                if (_MapTo.ContainsKey(item))
                {
                    Item i = _MapTo[item] as Item;
                    if (i != null && i.ParentModule != null)
                    {
                        i.ParentModule._MapTo.Remove(i);
                        i.ParentModule._MapFrom.Remove(i);
                    }

                    _MapTo.Remove(item);
                    _MapFrom.Remove(item);
                }
                return;
            }
            
            if (!IsParentOf(item) || (item != target && IsParentOf(target))) return;

            _MapTo[item] = target;
            target.ParentModule._MapFrom[target] = item;
        }

        /// <summary>
        /// Substitute one item for another inside this module
        /// </summary>
        /// <param name="oldItem">node to replace</param>
        /// <param name="newItem">new node</param>
        public virtual void SubstituteItem(Item oldItem, Item newItem)
        {
            Hashtable items = base.AllItems;

            foreach (Item i in items.Values)
            {
                if (oldItem is Node && newItem is Node)
                {
                    if (i is Edge)
                    {
                        ((Edge)i).SubstituteNode((Node)oldItem, (Node)newItem);
                    }
                    if (i is Part)
                    {
                        Part p = (Part)i;
                        if (p.PoPS_Left != null) p.PoPS_Left.SubstituteNode((Node)oldItem, (Node)newItem);
                        if (p.PoPS_Right != null) p.PoPS_Right.SubstituteNode((Node)oldItem, (Node)newItem);
                    }
                }
            }

            foreach (Item i in _Items.Values)
            {
                if (i is Container)
                {
                    if (((Container)i).Contains(oldItem))
                    {
                        ((Container)i).RemoveItem(oldItem);
                        ((Container)i).AddItem(newItem);
                    }
                }
            }
        }

        /// <summary>
        /// All the exposed items
        /// </summary>
        public virtual List<Item> ExposedItems
        {
            get
            {
                /*if (_Temporary != null)
                {
                    foreach (Edge e in _Temporary)
                        e.Remove();
                    _Temporary.Clear();
                }*/

                Hashtable items = base.AllItems;
                List<Item> list = new List<Item>();
                foreach (Item n in items.Values)
                {
                    if (n is Node)  //only nodes can be exposed
                    {
                        List<Edge> edges = ((Node)n).AllEdges;
                        foreach (Edge e in edges)
                        {
                            if (e != null && e.Parent != null && !IsParentOf(e))
                            {
                                list.Add(n);
                                break;
                            }
                        }
                    }
                    if (!list.Contains(n) && (n is Part))
                    {
                        Part p = (Part)n;
                        if (p.Left != null && !items.ContainsValue(p.Left))
                            list.Add(n);
                        else
                            if (p.Right != null && !items.ContainsValue(p.Right))
                                list.Add(n);
                    }
                }
                if (_MapTo != null)
                {
                    List<Item> toRemove = new List<Item>();
                    foreach (Item i in _MapTo.Keys)
                    {
                        if (!(_MapTo[i] is Node) || ((Node)_MapTo[i]).ParentModule == null)  //bad
                            toRemove.Add(i);
                        else
                            if (!list.Contains(i))
                                list.Add(i);
                    }
                    foreach (Item i in toRemove)
                        _MapTo.Remove(i);
                }
                if (_MapFrom != null)
                {
                    List<Item> toRemove = new List<Item>();
                    foreach (Item i in _MapFrom.Keys)
                    {
                        if (!(_MapFrom[i] is Node) || ((Node)_MapFrom[i]).ParentModule == null)  //bad
                            toRemove.Add(i);
                        else
                            if (!list.Contains(i))
                                list.Add(i);
                    }
                    foreach (Item i in toRemove)
                        _MapFrom.Remove(i);
                }
                return list;
            }
        }

        static List<Edge> _Temporary = new List<Edge>();

        /// <summary>
        /// returns all the items that are contained in this Module.
        /// This function will perform neccessary substitutions for module connections.
        /// Avoid frequent calls to this method, as it is O(n)
        /// </summary>
        /// <returns></returns>
        public override Hashtable AllItems
        {
            get
            {
                /*if (_Temporary == null)
                    _Temporary = new List<Edge>();
                else
                {
                    foreach (Edge e in _Temporary)
                        e.Remove();
                    _Temporary.Clear();
                }*/

                Hashtable items = new Hashtable();
                Hashtable mappings = new Hashtable();
                Hashtable uniqueIDs = new Hashtable();

                if (_Items != null && _Items.Count > 0)
                {
                    foreach (string key in _Items.Keys)
                    {
                        Item i = _Items[key] as Item;
                        if (i != null && i != null)
                        {
                            items[key] = i;
                            uniqueIDs[key] = 1;
                        }
                    }

                    foreach (string key in _Items.Keys)
                    {
                        Item i = _Items[key] as Item;
                        if (i != null && i != null)
                        {
                            if (i is Container)
                            {
                                if (i is Module)
                                {
                                    Module m = (Module)i;
                                    //List<Item> exposed = m.ExposedItems;

                                    if (m._MapTo.Count > 0)
                                    {
                                        foreach (Item i2 in m._MapTo.Keys)
                                        {
                                            Item t = m.GetMappingTo(i2);
                                            if (t != null && t != i2 && IsParentOf(t))
                                            {
                                                mappings[i2] = t;
                                            }
                                        }
                                    }
                                }

                                Hashtable subcomponents = ((Container)i).AllItems;
                                foreach (string str in subcomponents.Keys)
                                {
                                    string s = str;

                                    if (i is Compartment) s = key + "_" + str;

                                    Object o = subcomponents[str];

                                    if (o is Item)
                                    {
                                        Item n = (Item)o;
                                        Item n2 = n;
                                        if (uniqueIDs.ContainsKey(s))
                                        {
                                            if (items.Contains(s))
                                            {
                                                n2 = (Item)items[s];  //the item with same name
                                                if (n.Depth >= n2.Depth)
                                                {
                                                    Item parent = n.Parent;
                                                    if (parent != null)
                                                    {
                                                        string s2 = n.ID;
                                                        while (items.ContainsKey(s2))
                                                        {
                                                            s2 = parent.ID + "_" + s2;
                                                            parent = parent.Parent;
                                                            if (parent == null || parent == n.Parent)
                                                                break;
                                                        }
                                                        items[s2] = n;
                                                    }
                                                }
                                                else
                                                {
                                                    items[s] = n;
                                                    Item parent = n2.Parent;
                                                    if (parent != null)
                                                    {
                                                        string s2 = n2.ID;
                                                        while (items.ContainsKey(s2))
                                                        {
                                                            s2 = parent.ID + "_" + s2;
                                                            parent = parent.Parent;
                                                            if (parent == null || parent == n2.Parent)
                                                                break;
                                                        }
                                                        items[s2] = n2;
                                                    }
                                                }
                                            }
                                            else
                                            {
                                                items[s] = n;
                                            }

                                            n2 = (Item)items[s];
                                            if (n2.ID == s && n2.Parent != this)
                                            {
                                                Item parent = n2.Parent;
                                                if (parent != null)
                                                {
                                                    string s2 = parent.ID + "_" + n2.ID;
                                                    parent = parent.Parent;
                                                    while (items.ContainsKey(s2))
                                                    {
                                                        s2 = parent.ID + "_" + s2;
                                                        parent = parent.Parent;
                                                        if (parent == null || parent == n2.Parent)
                                                            break;
                                                    }
                                                    items.Remove(s);
                                                    items[s2] = n2;
                                                }
                                            }
                                        }
                                        else
                                        {
                                            items[s] = n;
                                        }
                                        uniqueIDs[s] = 1;
                                    }
                                }
                            }
                        }
                    }
                }

                foreach (Item i in mappings.Keys)   //for each mapped node
                {
                    if (i is Node && items.ContainsValue(i)) //if its valid
                    {
                        /*
                        List<Edge> edges = ((Node)i).AllEdges;  //get all the reactions involved
                        foreach (Edge e in edges)       //for each reaction that uses that node
                        {
                            if (items.ContainsValue(e))     //replace that reaction by..
                            {
                                Edge e2 = (Edge)e.Clone();   //make a duplicate

                                List<Node> nodes = e2.Nodes;  //make all the needed substitutions
                                foreach (Node n in nodes)   //for each node in the reaction
                                {
                                    foreach (Item i2 in mappings.Keys) //if it has a mapping
                                    {
                                        if (n == i2)
                                        {
                                            Node t = mappings[i2] as Node;
                                            if (t != null && IsParentOf(t))
                                            {
                                                string keyt = "";
                                                foreach (string s in items.Keys)
                                                    if (items[s] == t)
                                                    {
                                                        keyt = s;
                                                        break;
                                                    }
                                                //Node t2 = t.Clone() as Node;
                                                e2.SubstituteNode(n, t);  //substitute
                                                _Temporary.Add(e2);
                                                //items[keyt] = t2;
                                            }
                                        }
                                    }
                                }

                                string key = "";   //need the reaction key to remove it
                                foreach (string s in items.Keys)  //get the reaction key
                                {
                                    if (items[s] == e)
                                    {
                                        key = s;
                                        break;
                                    }
                                }
                                if (!string.IsNullOrEmpty(key))
                                {
                                    items.Remove(key);         //remove old reaction
                                    items[key] = e2;      //add in new reaction
                                    //_Items[key] = e2;
                                    //e2.Parent = this;    //set parent
                                    //_Items.Remove(key);
                                }
                            }   //done...phew!
                        }
                        */
                        Node target = mappings[i] as Node;
                        if (target != null)
                        {
                            string key = "";
                            foreach (string s in items.Keys)    //remove the original
                            {
                                if (items[s] == i)
                                {
                                    key = s;
                                    break;
                                }
                            }
                            if (!string.IsNullOrEmpty(key))
                            {
                                items.Remove(key);
                            }
                        }
                    }
                }

                return items;
            }
        }


        protected Hashtable _Parameters = new Hashtable();
        /// <summary>
        /// get all the parameters for just this module (excluding modules within modules)
        /// </summary>
        public virtual Hashtable LocalParameters
        {
            get { return _Parameters; }
            set { _Parameters = value; }
        }

        /// <summary>
        /// Get a hashtable of string->Parameter pairs for all the parameters in this module
        /// (includes parameters in sub-modules)
        /// </summary>
        public virtual Hashtable GlobalParameters
        {
            get 
            {
                Hashtable parameters = new Hashtable();

                foreach (string s in _Parameters.Keys)
                {
                    if (!(_Parameters[s] is Parameter))
                        _Parameters[s] = new Parameter(1, this);

                    parameters[s] = (Parameter)_Parameters[s];
                }

                Hashtable items = base.AllItems;
                foreach (string id in items.Keys) //for all modules
                {
                    if (items[id] is Module)
                    {
                        Module mod = (Module)items[id];
                        foreach (string s in mod._Parameters.Keys)
                        {
                            if (!(mod._Parameters[s] is Parameter))
                                mod._Parameters[s] = new Parameter(1, this);

                            parameters[id + "_" + s] = (Parameter)mod._Parameters[s];
                            
                        }
                    }
                }

                return parameters;
            }
        }

        /// <summary>
        /// Obtain a parameter name that is not in use. This method iterates through
        /// the series of strings k1,k2,k3....kn until it finds a name kn that is not in use
        /// </summary>
        /// <returns></returns>
        public virtual string RequestNewParameterName(string prefix)
        {
            int i = 0;

            Hashtable allParams = GlobalParameters, allRules = GlobalRules;

            while (allParams.ContainsKey(prefix + i) || allRules.ContainsKey(prefix + i)) ++i;

            return prefix + i;
        }

        /// <summary>
        /// Assign new parameters to the Edge if it has parameter names that conflict with
        /// the module's parameters. The Edge's parameters are recorded in the module as well
        /// </summary>
        /// <param name="edge">target edge to check</param>
        public virtual void AssignParametersToReaction(Edge edge)
        {
            if (edge == null) return;
            List<string> names = edge.ParameterNames;
            for (int j = 0; j < names.Count; ++j)
            {
                if (LocalParameters.Contains(names[j])) //if this parameter name is in use
                {
                    if ((names[j][names[j].Length - 1] >= (int)'0' && names[j][names[j].Length - 1] <= (int)'9')
                        || LocalParameters.Contains(edge.ID + "_" + names[j]))  //is ID + param is in use?
                    {
                        edge.ChangeName(names[j], RequestNewParameterName(names[j].Substring(0,names[j].Length - 1))); //request a completely new unique param name
                    }
                    else   
                    {
                        edge.ChangeName(names[j], edge.ID + "_" + names[j]);  //id _old parm
                    }
                }
                _Parameters[names[j]] = new Parameter(1.0,this);  //synchronize the module's parameter
            }
            UpdateParameters();
        }

        /// <summary>
        /// populate the parameters hashtable by looking at all the reactions
        /// </summary>
        public virtual void UpdateParameters()
        {
            if (_Parameters == null || _Items == null || _Items.Count < 1 || NOUPDATES) return;

            Hashtable allItems = base.AllItems;

            List<Edge> edges = new List<Edge>();
            foreach (Item i in allItems.Values)
            {
                if (i.ParentModule == this)
                {
                    if (i is Edge)
                        edges.Add((Edge)i);
                    else
                        if (i is Part)
                        {
                            if (((Part)i).PoPS_Left != null)
                                edges.Add(((Part)i).PoPS_Left);
                            if (((Part)i).PoPS_Right != null)
                                edges.Add(((Part)i).PoPS_Right);
                        }
                }
            }

            List<string> currentParams = new List<string>();
            foreach (string s in _Parameters.Keys)
                currentParams.Add(s);

            foreach (Edge e in edges)
            {
                List<string> parms = e.ParameterNames;
                foreach (string s in parms)
                {
                    if (!_Rules.ContainsKey(s))
                    {
                        currentParams.Remove(s);
                        if (!_Parameters.Contains(s))
                            _Parameters[s] = new Parameter(1.0, this);
                    }
                }
            }
            foreach (string s in currentParams)
                _Parameters.Remove(s);
        }

        /// <summary>
        /// Updates all the edges in this module as well as the parameters
        /// </summary>
        public virtual void Update()
        {
            NOUPDATES = true;
            foreach (Item i in _Items.Values)
            {
                if (i is Edge)
                {
                    ((Edge)i).Update();
                }
                if (i is Part)
                {
                    ((Part)i).PoPS_Left.Update();
                    ((Part)i).PoPS_Right.Update();
                }
                if (i is Module)
                {
                    ((Module)i).Update();
                }
            }
            NOUPDATES = false;
            UpdateParameters();
        }

        /// <summary>
        /// Update parameters by using the given module to fill-in the values
        /// </summary>
        /// <param name="borrowee"></param>
        public virtual void UpdateParameters(Module borrowee)
        {
            if (borrowee == null || borrowee == this) UpdateParameters();
            Hashtable allItems = base.AllItems;

            List<Edge> edges = new List<Edge>();
            foreach (Item i in allItems.Values)
            {
                if (i.ParentModule == this)
                {
                    if (i is Edge)
                        edges.Add((Edge)i);
                    else
                        if (i is Part)
                        {
                            if (((Part)i).PoPS_Left != null)
                                edges.Add(((Part)i).PoPS_Left);
                            if (((Part)i).PoPS_Right != null)
                                edges.Add(((Part)i).PoPS_Right);
                        }
                }
            }

            List<string> currentParams = new List<string>();
            foreach (string s in _Parameters.Keys)
                currentParams.Add(s);

            foreach (Edge e in edges)
            {
                List<string> parms = e.ParameterNames;
                foreach (string s in parms)
                {
                    if (!_Rules.ContainsKey(s))
                    {
                        currentParams.Remove(s);
                        if (!_Parameters.Contains(s))
                        {
                            if (borrowee._Parameters.Contains(s))
                                _Parameters[s] = (Parameter)borrowee._Parameters[s];
                            else
                                _Parameters[s] = new Parameter(1.0, this);
                        }
                    }
                }
            }
            foreach (string s in currentParams)
                _Parameters.Remove(s);
        }
        
        /// <summary>
        /// Get a parameter value
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public virtual double GetParameter(string name)
        {
            if (_Parameters != null && _Parameters.Contains(name))
            {
                if (!(_Parameters[name] is Parameter))
                    _Parameters[name] = new Parameter(1, this);

                return ((Parameter)_Parameters[name]).Value;
            }

            Hashtable items = base.AllItems;
            foreach (string id in items.Keys) //for all modules
            {
                if (items[id] is Module)
                {
                    Module mod = (Module)items[id];
                    foreach (string s in mod._Parameters.Keys)
                    {
                        if (name.Equals(id + "_" + s))
                        {
                            if (!(mod._Parameters[s] is Parameter))
                                mod._Parameters[s] = new Parameter(1, this);

                            return ((Parameter)mod._Parameters[s]).Value;
                        }
                    }
                }
            }
            //no match? then make this parameter:
            _Parameters[name] = new Parameter(1, this);

            return 1;
        }

        /// <summary>
        /// Set a parameter value in the module
        /// </summary>
        /// <param name="name"></param>
        /// <param name="value"></param>
        public virtual void SetParameter(string name, double value)
        {
            try
            {
                if (_Parameters == null) _Parameters = new Hashtable();

                if (!ValidID(name)) return;

                if (_Parameters.Count > 0 && _Parameters.Contains(name))
                {
                    if (!(_Parameters[name] is Parameter))
                        _Parameters[name] = new Parameter(1, this);

                    ((Parameter)_Parameters[name]).Value = value;
                }

                Hashtable items = base.AllItems;
                foreach (string id in items.Keys) //for all modules
                {
                    if (items[id] is Module)
                    {
                        Module mod = (Module)items[id];
                        foreach (string s in mod._Parameters.Keys)
                        {
                            if (name.Equals(id + "_" + s))
                            {
                                if (!(mod._Parameters[s] is Parameter))
                                    mod._Parameters[s] = new Parameter(1, this);

                                ((Parameter)mod._Parameters[s]).Value = value;
                            }
                        }
                    }
                }
                //no match? then make this parameter:
                
                _Parameters[name] = new Parameter(value, this);
                
            }
            catch (Exception) 
            { 
                //stupid hashtables
            }
        }

        /// <summary>
        /// Changes the name of a local parameter
        /// </summary>
        /// <param name="oldname"></param>
        /// <param name="newname"></param>
        public virtual void ChangeParameterName(string oldname, string newname)
        {
            if (ValidID(newname) && _Parameters.Contains(oldname) && !GlobalParameters.Contains(newname) && !AllItems.Contains(newname) && !GetRules(this).ContainsKey(newname))
            {
                _Parameters[newname] = _Parameters[oldname];
                _Parameters.Remove(oldname);
            }
            else
            {
                return;
            }

            NOUPDATES = true;

            foreach (Edge e in LocalReactions)
            {
                e.ChangeParameterName(oldname, newname);
            }
            foreach (Edge e in LocalRegulations)
            {
                e.ChangeParameterName(oldname, newname);
            }
            foreach (Part p in LocalParts)
            {
                p.PoPS_Left.ChangeParameterName(oldname, newname);
                p.PoPS_Right.ChangeParameterName(oldname, newname);
            }
            NOUPDATES = false;
            UpdateParameters();
        }

        /// <summary>
        /// Changes the name of a local rule
        /// </summary>
        /// <param name="oldname"></param>
        /// <param name="newname"></param>
        public virtual void ChangeRuleName(string oldname, string newname)
        {
            if (ValidID(newname) && _Parameters.Contains(oldname) && !GlobalParameters.Contains(newname) && !AllItems.Contains(newname) && !GetRules(this).ContainsKey(newname))
            {
                _Rules[newname] = _Rules[oldname];
                _Rules.Remove(oldname);
            }
            else
            {
                return;
            }

            NOUPDATES = true;

            foreach (Edge e in LocalReactions)
            {
                e.ChangeParameterName(oldname, newname);
            }
            foreach (Edge e in LocalRegulations)
            {
                e.ChangeParameterName(oldname, newname);
            }
            foreach (Part p in LocalParts)
            {
                p.PoPS_Left.ChangeParameterName(oldname, newname);
                p.PoPS_Right.ChangeParameterName(oldname, newname);
            }
            NOUPDATES = false;
            UpdateParameters();
        }
        /// <summary>
        /// Returns a string with parameter names and values separated by ;
        /// Eg: k1 = 0.1; k2 = 1.5; k3 = 0.5; k0 = 0.1;
        /// </summary>
        /// <returns></returns>
        public virtual string GetParameterInitializations(string sep)
        {
            //UpdateParameters();
            string sParameterInitializations = "";
            foreach (string s in _Parameters.Keys)
            {
                sParameterInitializations += string.Format("{0} = {1}{2}", s, _Parameters[s],sep);
            }

            Hashtable items = base.AllItems;
            foreach (string id in items.Keys) //for all modules
            {
                if (items[id] is Module)
                {
                    Module mod = (Module)items[id];
                    foreach (string s in mod._Parameters.Keys)
                        sParameterInitializations += string.Format("{0} = {1}{2}", id + "_" + s, mod._Parameters[s],sep);
                }
            }

            return sParameterInitializations;
        }
        
        /// <summary>
        /// Adds the item to the module after checking for name conflicts
        /// </summary>
        /// <param name="item"></param>
        /// <returns></returns>
        public override bool AddItem(Item item)
        {
            if (item == null || item == this || item is GlobalSpecies) return true;
            
            return AddItem(item,item.ParentModule);
        }

        /// <summary>
        /// Adds the item to the module after checking for name conflicts
        /// </summary>
        /// <param name="item"></param>
        /// <returns></returns>
        public override bool AddItem(Item item, Item oldParent)
        {
            if (item == null || item == this || item is GlobalSpecies) return true;

            if (oldParent != null && !(oldParent is Module)) oldParent = oldParent.ParentModule;

            if (_Items.ContainsValue(item))
            {
                if (item.Parent != this) item.Parent = this;
                return true;
            }

            if (_Items == null) _Items = new Hashtable();

            if (item == null || item == this || item is GlobalSpecies) return true;

            if (_Items.ContainsValue(item))
            {
                if (item.Parent != this) item.Parent = this;
                return true;
            }

            if (_Items.ContainsKey(item.ID)) item.ID = RequestNewID(item.ID);

            if (!ValidID(item.ID)) return false;

            _Items[item.ID] = item;

            if (item.Parent != this) item.Parent = this;

            if (oldParent != this && (item is Edge || item is Part))
            {
                UpdateParameters(oldParent as Module);
                if (item is Edge)
                    ((Edge)item).Update();
                if (item is Part)
                {
                    ((Part)item).PoPS_Left.Update();
                    ((Part)item).PoPS_Right.Update();
                }
            }

            return true;
        }

        /// <summary>
        /// Removes the item and updates parameters if the item is an Edge
        /// </summary>
        /// <param name="item"></param>
        public override void RemoveItem(Item item)
        {
            base.RemoveItem(item);
            if (item is Edge || item is Part) UpdateParameters();
        }

        /// <summary>
        /// Removes the item and updates parameters if the item is an Edge
        /// </summary>
        /// <param name="item"></param>
        public override void RemoveItem(Item item, Item newParent)
        {
            base.RemoveItem(item);
            if (newParent is Module && (item is Edge || item is Part)) UpdateParameters();
        }

        /// <summary>
        /// Checks whether this Container contains an item with the given ID
        /// </summary>
        /// <param name="ID"></param>
        /// <returns></returns>
        public virtual bool Conflicts(string ID)
        {
            if (_Items == null) _Items = new Hashtable();
            /*
            foreach (Item i in _Items.Values)
            {
                if (i is Module)
                {
                    if (((Module)i).Conflicts(ID))
                        return true;
                }
            }
            */
            return _Items.ContainsKey(ID);
        }

        /// <summary>
        /// Given a token from a particular module, this function will return
        /// a new token name (can be the same) that will make the token unique in this module. 
        /// </summary>
        /// <param name="id"></param>
        /// <param name="parent"></param>
        /// <returns></returns>
        public virtual string Rename(string name, Item parent)
        {
            return Rename(name, parent, AllItems);
        }
        /// <summary>
        /// This method is a time-saver version of Rename(string,Module).
        /// Instead of doing Rename(id,module), do the following:
        /// Hashtable H = module.AllItems; 
        /// Rename(id, module, H). Avoid recalculating AllItems.
        /// </summary>
        /// <param name="name">name of item to rename</param>
        /// <param name="itemParent">parent of that item</param>
        /// <param name="items">module's Allitems</param>
        /// <returns></returns>
        public virtual string Rename(string name, Item itemParent, Hashtable items)
        {
            if (itemParent == null || items == null) return name;
            if (itemParent is Module)
                return Rename(name, itemParent, itemParent as Module, itemParent as Module, items, true);
            else
                return Rename(name, itemParent, itemParent.ParentModule, itemParent.ParentModule, items, true);
        }

        /// <summary>
        /// This method is a time-saver version of Rename(string,Module).
        /// Instead of doing Rename(id,module), do the following:
        /// Hashtable H = module.AllItems; 
        /// Rename(id, module, H). Avoid recalculating AllItems.
        /// </summary>
        /// <param name="name">name of item to rename</param>
        /// <param name="itemParent">parent of that item</param>
        /// <param name="items">module's Allitems</param>
        /// <param name="map">whether or not to map module equaivalence relations</param>
        /// <returns></returns>
        public virtual string Rename(string name, Item itemParent, Hashtable items, bool map)
        {
            if (itemParent == null || items == null) return name;
            if (itemParent is Module)
                return Rename(name, itemParent, itemParent as Module, itemParent as Module, items, map);
            else
                return Rename(name, itemParent, itemParent.ParentModule, itemParent.ParentModule, items, map);
        }

        protected virtual string Rename(string name, Item itemParent, Module parent, Module placeHolder, Hashtable items, bool map)
        {
            if (parent == null || items == null) return name;

            if (map && parent._MapTo != null && parent._MapTo.Count > 0)
            {
                foreach (Item i1 in parent._MapTo.Keys)
                {
                    if (i1.ID == name)
                    {
                        Item i2 = parent._MapTo[i1] as Item;
                        if (i2 != i1 && i2 != null && IsParentOf(i2))
                        {
                            name = i2.ID;
                            parent = i2.ParentModule;
                            if (parent != placeHolder)
                                return Rename(name, itemParent, parent, placeHolder, items, map);
                        }
                    }
                }
            }

            string candidate = name;   //possible match

            foreach (string id in items.Keys)
            {
                Item item = items[id] as Item;
                if (item != null && item.ID.Equals(name))
                {
                    if (item.Parent == parent)
                        return id;  //same parent, same id -- this is the item
                    else
                        candidate = id;   //matches the id, but not the same parent
                }

                if (item == parent)  //search parameters of a module
                {
                    Module mod = parent;
                    foreach (string s in mod._Parameters.Keys)
                    {
                        if (name.Equals(s))
                        {
                            if (parent == this)
                                return name;
                            else
                                return id + "_" + name;
                        }
                    }
                    foreach (string s in mod._Rules.Keys)
                    {
                        if (name.Equals(s))
                        {
                            if (parent == this)
                                return name;
                            else
                                return id + "_" + name;
                        }
                    }
                }
            }
            return candidate;  //nowhere to be found
        }

        /// <summary>
        /// Checks whether items are crosstalking inside this module.
        /// In order for crosstalk to occur, two copies of the same molecule (DBID)
        /// must be present in the same compartment
        /// </summary>
        public virtual List<string> CrossTalk()
        {
            Hashtable allitems = AllItems;

            return CrossTalk(allitems);

        }

        public virtual List<string> CrossTalk(Hashtable allitems)
        {
            List<string> crosstalks = new List<string>();

            if (allitems == null) return crosstalks;

            string[] keys = new string[allitems.Count];
            int i = 0;
            foreach (string s in allitems.Keys)
            {
                keys[i] = s;
                ++i;
            }

            for (i=0; i < keys.Length-1; ++i)
            {
                string s1 = keys[i];
                Item p1 = allitems[s1] as Item;
                for (int j=i+1; j < keys.Length; ++j)
                {
                    string s2 = keys[j];
                    Item p2 = allitems[s2] as Item;
                    if (p1 != null && p2 != null && p1 != p2 && 
                        !string.IsNullOrEmpty(p1.DBID) && !string.IsNullOrEmpty(p2.DBID) &&
                        p1.DBID == p2.DBID)
                    {
                        Item c1 = p1.ParentOfType(typeof(Compartment));
                        Item c2 = p2.ParentOfType(typeof(Compartment));
                        if (c1 == c2 || (c1 == null && c2 == null))
                        {
                            crosstalks.Add(s1 + " and " + s2);
                        }
                    }
                }
            }
            return crosstalks;
        }

        /// <summary>
        /// Remove this module, clear its parameters
        /// </summary>
        public override void Remove()
        {
            base.Remove();
            _Parameters.Clear();
        }

        protected Hashtable _Rules = new Hashtable();
        /// <summary>
        /// name of rule -> string for that rule
        /// </summary>
        public virtual Hashtable LocalRules
        {
            get { return _Rules; }
            set { _Rules = value; }
        }

        /// <summary>
        /// Set a new rule in this module. Nothing will happen if the same name is used elsewhere
        /// in the module
        /// </summary>
        /// <param name="name"></param>
        /// <param name="rule"></param>
        public virtual void SetRule(string name, string rule)
        {
            if (AllItems.ContainsKey(name) || GlobalParameters.ContainsKey(name)) return;

            if (_Rules.ContainsKey(name) && string.IsNullOrEmpty(rule))
            {
                _Rules.Remove(name);
                UpdateParameters();
                return;
            }

            try
            {
                Hashtable allParams = GlobalParameters;
                Functions.TAlgExpression ae = new Functions.TAlgExpression();
                ae.AssignBehaviour = Functions.TAssignBehaviour.eInstallSymbols;
                ae.compile(rule);
                Functions.TTreeNode[] nodes = ae.GetAllSymbols();

                foreach (Functions.TTreeNode tn in nodes)
                {
                    Item item = FindItem(tn.name);
                    if (!(item is Species) || allParams.ContainsKey(tn.name))
                    {
                        return;
                    }
                }
            }
            catch (Exception)
            {
                return;
            }

            _Rules[name] = rule;
        }

        /// <summary>
        /// Get the rules of this module (and all modules within it) in the context of another module
        /// </summary>
        /// <param name="module"></param>
        /// <returns></returns>
        public virtual Hashtable GlobalRules
        {
            get
            {
                return GetRules(this);
            }
        }
        /// <summary>
        /// Get the rules of this module (and all modules within it) in the context of another module
        /// </summary>
        /// <param name="module"></param>
        /// <returns></returns>
        public virtual Hashtable GetRules(Module module)
        {
            if (module == null) return _Rules;

            try
            {

                Hashtable allItems = module.AllItems;

                Hashtable rules = new Hashtable();

                if (_Rules == null) return rules;

                foreach (string s in _Rules.Keys)
                {
                    string rule = ' ' + (string)_Rules[s] + ' ';

                    string[] ops = Functions.TScanner.Operators;
                    foreach (string op in ops)
                    {
                        rule = rule.Replace(op, ' ' + op + ' ');
                    }
                    string[] words = rule.Split(' ');

                    foreach (string str in words)
                    {
                        string word = str.Trim();
                        if (!(word.Length == 0 ||
                                word.ToLower().Equals("pi") || word.ToLower().Equals("exponentiale") ||
                                word.Equals("(") || word.Equals(")") || word.Equals(",") ||
                                word.Equals("-") || word.Equals("+") || word.Equals("*") ||
                                word.Equals("/") || word.Equals("^")))
                        {
                            string newword = word;
                            newword = module.Rename(word, this, allItems);
                            rule = rule.Replace(' ' + word + ' ', newword);
                        }
                    }
                    rule = rule.Replace(" ", "");
                    rules[s] = rule;
                }

                Hashtable thisAllItems = AllItems;

                foreach (Item i in _Items.Values)
                {
                    if (i is Module)
                    {
                        Hashtable subrules = ((Module)i).GetRules(this, thisAllItems);
                        foreach (string s in subrules)
                        {
                            if (!_Rules.Contains(i.ID + "_" + s))
                                rules[i.ID + "_" + s] = (string)subrules[s];
                        }
                    }
                }
                return rules;
            }
            catch (Exception)
            {
                return _Rules;
            }
        }

        /// <summary>
        /// Get the rules of this module in the context of another module
        /// </summary>
        /// <param name="module"></param>
        /// <returns></returns>
        public virtual Hashtable GetRules(Module module, Hashtable allItems)
        {
            if (module == null) return _Rules;

            Hashtable rules = new Hashtable();

            if (_Rules == null) return rules;

            foreach (string s in _Rules)
            {
                string rule = ' ' + (string)_Rules[s] + ' ';

                string[] ops = Functions.TScanner.Operators;
                foreach (string op in ops)
                {
                    rule = rule.Replace(op, ' ' + op + ' ');
                }
                string[] words = rule.Split(' ');

                foreach (string str in words)
                {
                    string word = str.Trim();
                    if (!(word.Length == 0 ||
                            word.ToLower().Equals("pi") || word.ToLower().Equals("exponentiale") ||
                            word.Equals("(") || word.Equals(")") || word.Equals(",") ||
                            word.Equals("-") || word.Equals("+") || word.Equals("*") ||
                            word.Equals("/") || word.Equals("^")))
                    {
                        string newword = word;
                        newword = module.Rename(word, this, allItems);
                        rule = rule.Replace(' ' + word + ' ', newword);
                    }
                }
                rule = rule.Replace(" ", "");
                rules[s] = rule;
            }

            Hashtable thisAllItems = AllItems;

            foreach (Item i in _Items.Values)
            {
                if (i is Module)
                {
                    Hashtable subrules = ((Module)i).GetRules(this, thisAllItems);
                    foreach (string s in subrules)
                    {
                        if (!_Rules.Contains(i.ID + "_" + s))
                            rules[i.ID + "_" + s] = (string)subrules[s];
                    }
                }
            }

            return rules;
        }
        public virtual string ModularScript
        {
            get
            {
                StringBuilder sModel = new StringBuilder();

                List<Species> species = LocalSpecies;
                List<Part> parts = LocalParts;
                List<Edge> edges = LocalReactions;
                List<Module> modules = LocalModules;

                //define all the contained modules

                if (modules.Count > 0)
                {
                    foreach (Module mod in modules)
                    {
                        sModel.Append(mod.ModularScript);
                    }
                    sModel.Append(System.Environment.NewLine);
                }                

                sModel.Append("model " + ID);
                sModel.Append(System.Environment.NewLine);

                //Variable declarations

                if (modules.Count > 0)   //add I/O items from each sub-module into this module
                {
                    sModel.Append("   modules ");
                    for (int i = 0; i < modules.Count; ++i)
                    {
                        if (i > 0)
                            sModel.Append("," + modules[i].ID);
                        else
                            sModel.Append(modules[i].ID);
                    }
                    sModel.Append(System.Environment.NewLine);
                }

                List<string> boundarySpecies = new List<string>();
                if (species.Count > 0)
                {
                    sModel.Append("   species " + species[0].ID);
                    if (species[0].Boundary) boundarySpecies.Add(species[0].ID);
                    for (int i=1; i < species.Count; ++i)
                    {
                        sModel.Append("," + species[i].ID);
                        if (species[i].Boundary) boundarySpecies.Add(species[i].ID);
                    }
                    sModel.Append(System.Environment.NewLine);
                }

                List<string> partTypes = new List<string>();
                foreach (Part p in parts)
                    if (!partTypes.Contains(p.ItemType))
                        partTypes.Add(p.ItemType);

                if (parts.Count > 0)
                {
                    foreach (string type in partTypes)
                    {
                        sModel.Append("  " + type + " ");
                        bool first = true;
                        foreach (Part p in parts)
                        {
                            if (p.ItemType == type)
                            {
                                if (first)
                                {
                                    sModel.Append(p.ID);
                                    first = false;
                                }
                                else
                                    sModel.Append("," + p.ID);
                            }
                        }
                        sModel.Append(System.Environment.NewLine);
                    }
                }

                //boundary species
                if (boundarySpecies.Count > 0)
                {
                    sModel.Append("   const " + boundarySpecies[0]);
                    for (int i = 1; i < boundarySpecies.Count; ++i)
                    {
                        sModel.Append("," + boundarySpecies[i]);
                    }
                    sModel.Append(System.Environment.NewLine);
                }

                //PoPS 

                if (parts.Count > 0)
                {
                    for (int i = 0; i < parts.Count; ++i)
                    {
                        sModel.Append("   " + parts[i].ID + ": " + parts[i].PoPS.Rate + ";");
                        sModel.Append(System.Environment.NewLine);
                    }
                }

                //Reactions

                if (edges.Count > 0)
                {
                    for (int i = 0; i < edges.Count; ++i)
                    {
                        string e = edges[i].ToString().Replace(Empty.ID, " ");
                        sModel.Append("   " + e);
                        sModel.Append(System.Environment.NewLine);
                    }
                    sModel.Append(System.Environment.NewLine);
                }

                //modules -- the tough part

                if (modules.Count > 0)
                {
                    Hashtable allItems = AllItems;
                    for (int i = 0; i < modules.Count; ++i)
                    {
                        Module m = modules[i];
                        sModel.Append("   " + m.ID + ": " + m.ID);

                        List<Item> exposed = m.ExposedItems;
                        sModel.Append("(");
                        if (exposed != null && exposed.Count > 0)
                        {
                            bool first = true;
                            foreach (Item it in exposed)
                            {
                                Item t = GetMappingTo(it);
                                if (t != null && IsParentOf(t) && it.ParentModule != null && t != it)
                                {
                                    if (first)
                                    {
                                        first = false;
                                    }
                                    else
                                    {
                                        sModel.Append(",");
                                    }
                                    sModel.Append(it.ParentModule.Rename(it.ID, it.Parent) + "=" + Rename(t.ID,t.Parent,allItems));
                                }
                            }
                        }
                        sModel.Append(")");
                        sModel.Append(System.Environment.NewLine);
                    }
                    sModel.Append(System.Environment.NewLine);
                }

                //parameter initialization

                foreach (string par in _Parameters.Keys)
                {
                    sModel.Append("   " + par + " = " + _Parameters[par]);
                    sModel.Append(System.Environment.NewLine);
                }
                foreach (Species spe in species)
                {
                    sModel.Append("   " + spe.ID + " = " + spe.Concentration);
                    sModel.Append(System.Environment.NewLine);
                }
                sModel.Append("end");
                sModel.Append(System.Environment.NewLine);

                return sModel.ToString();
            }
        }

        /// <summary>
        /// Get the modular script version of this module
        /// In this script, all the submodules are combined
        /// into one model. Use ModularScript if you want to see
        /// the submodules
        /// </summary>
        public virtual string ModularScriptCombined
        {
            get
            {
                StringBuilder sModel = new StringBuilder();

                Hashtable allspecies = AllSpecies;
                
                Hashtable allparts = AllParts;
                
                Hashtable alledges = AllReactions;
                
                //define all the contained modules

                sModel.Append("model " + ID);
                sModel.Append(System.Environment.NewLine);

                //Variable declarations

                if (allspecies.Count > 0)
                {
                    sModel.Append("   species ");
                    bool first = true;
                    foreach (string s in allspecies.Keys)
                    {
                        if (first)
                        {
                            sModel.Append(s);
                            first = false;
                        }
                        else
                            sModel.Append("," + s);
                    }
                    sModel.Append(System.Environment.NewLine);
                }

                //parts
                if (allparts.Count > 0)
                {
                    List<string> partTypes = new List<string>();
                    foreach (Part p in allparts.Values)
                        if (!partTypes.Contains(p.ItemType))
                            partTypes.Add(p.ItemType);

                    foreach (string type in partTypes)
                    {
                        sModel.Append("  " + type + " ");
                        bool first = true;
                        foreach (string s in allparts.Keys)
                        {
                            if (allparts[s] is Part && ((Part)(allparts[s])).ItemType == type)
                            {
                                if (first)
                                {
                                    sModel.Append(s);
                                    first = false;
                                }
                                else
                                    sModel.Append("," + s);
                            }
                        }
                        sModel.Append(System.Environment.NewLine);
                    }
                }

                //PoPS 
                List<Part> visited = new List<Part>();
                if (allparts.Count > 0)
                {
                    foreach (Part part in allparts.Values)
                    {
                        if (!visited.Contains(part))
                        {
                            sModel.Append("   ");
                            Part leftmost = part.LeftMostGlobal;
                            Part p = leftmost;
                            while (p != null)
                            {
                                if (!visited.Contains(p) &&
                                    allparts.ContainsValue(p))
                                {
                                    visited.Add(p);
                                    string s = "";
                                    foreach (string s2 in allparts.Keys)
                                        if (allparts[s2] == p)
                                        {
                                            s = s2;
                                            break;
                                        }
                                    if (!string.IsNullOrEmpty(s))
                                        sModel.Append(s + ",");
                                }
                                p = p.Right;
                                if (p == leftmost) break;
                            }
                            sModel.Remove(sModel.Length - 1, 1);
                            sModel.Append(" : " + part.PoPS.Rate + ";");
                            sModel.Append(System.Environment.NewLine);
                        }
                    }
                }

                //Reactions
                if (alledges.Count > 0)
                {
                    foreach (Edge edge in alledges.Values)
                    {
                        string e = edge.ToString(this);
                        e.Replace("$SOURCE", " ");
                        e.Replace("$WASTE", " ");
                        sModel.Append("   " + e);
                        sModel.Append(System.Environment.NewLine);
                    }
                    sModel.Append(System.Environment.NewLine);
                }

                //parameter initialization
                Hashtable parameters = GlobalParameters;
                foreach (string par in parameters.Keys)
                {
                    sModel.Append("   " + par + " = " + parameters[par]);
                    sModel.Append(System.Environment.NewLine);
                }
                sModel.Append("end");
                sModel.Append(System.Environment.NewLine);

                return sModel.ToString();
            }
        }

        /// <summary>
        /// Get the Script version of this module
        /// </summary>
        public virtual string JarnacScript
        {
            get
            {
                List<string> oFloating = new List<string>();
                List<string> oBoundary = new List<string>();
                List<string> oRules = new List<string>();
                List<string> oReactions = new List<string>();
                List<string> oAssignments = new List<string>();

                IO.JarnacWriter.InitializeListsFromModel(this, oFloating, oBoundary, oRules, oReactions, oAssignments);

                StringBuilder sJarnac = new StringBuilder();

                sJarnac.Append("p = defn " + ID);
                sJarnac.Append(System.Environment.NewLine);

                for (int i = 0; i < oFloating.Count; i++)
                {
                    if (i == 0)
                        sJarnac.Append("\tvar ");

                    sJarnac.Append(oFloating[i]);

                    if (i == oFloating.Count - 1)
                        sJarnac.Append(";" + Environment.NewLine);
                    else
                        sJarnac.Append(", ");
                }

                for (int i = 0; i < oBoundary.Count; i++)
                {
                    if (i == 0)
                        sJarnac.Append("\text ");

                    sJarnac.Append(oBoundary[i]);

                    if (i == oBoundary.Count - 1)
                        sJarnac.Append(";" + Environment.NewLine);
                    else
                        sJarnac.Append(", ");
                }
                foreach (string s in oRules)
                {
                    sJarnac.Append("\t" + s);
                    sJarnac.Append(System.Environment.NewLine);
                }

                foreach (string s in oReactions)
                {
                    sJarnac.Append("\t" + s);
                    sJarnac.Append(System.Environment.NewLine);
                }

                sJarnac.Append(System.Environment.NewLine);
                sJarnac.Append("end;");
                sJarnac.Append(System.Environment.NewLine);

                foreach (string s in oAssignments)
                {
                    sJarnac.Append(s + ";");
                    sJarnac.Append(System.Environment.NewLine);
                }

                return sJarnac.ToString();
            }
        }

        /// <summary>
        /// Returns a Matrix class object that contains the 
        /// Stoichiometry matrix for this module. 
        /// </summary>
        public Functions.Matrix StoichiometryMatrix
        {
            get
            {
                Hashtable allitems = AllItems;


                Hashtable reactions = new Hashtable();
                Hashtable species = new Hashtable();

                foreach (string s in allitems.Keys)
                {
                    Item item = allitems[s] as Item;
                    if (item is Species && !((Species)item).GetBoundary(this))
                        species[s] = item;
                    else
                        if (item is Edge && ((Edge)item).IsReaction)
                            reactions[s] = item;
                }

                int numFloating = 0;
                foreach (string s in species.Keys)
                {
                    Species n = species[s] as Species;
                    if (n != null && !n.GetBoundary(this))
                        ++numFloating;
                }

                if (numFloating == 0 || reactions.Count == 0)
                    return new Athena.Functions.Matrix();

                Functions.Matrix N = new Athena.Functions.Matrix(numFloating, reactions.Count);
                N.columnNames = new string[reactions.Count];
                N.rowNames = new string[numFloating];

                int i = 0, j = 0;

                foreach (string s in species.Keys)
                {
                    N.rowNames[i] = s;
                    ++i;
                }

                i = j = 0;
                foreach (string r in reactions.Keys)
                {
                    N.columnNames[j] = r;

                    Edge e = reactions[r] as Edge;
                    if (e != null)
                    {
                        foreach (Node n in e.ReactantsHash.Keys) //for each reactant
                        {
                            Species ns = n as Species;
                            if (ns != null && !ns.GetBoundary(this))
                            {
                                string s = Rename(n.ID, n.Parent, allitems);  //map

                                for (i = 0; i < N.r; ++i)   //match with st.mat's row name
                                    if (N.rowNames[i] == s)
                                        break;
                                if (i < N.r)   //index valid
                                {
                                    N[i, j] -= (double)e.ReactantsHash[ns];
                                }
                            }
                        }
                        foreach (Node n in e.ProductsHash.Keys) //for each product
                        {
                            Species ns = n as Species;
                            if (ns != null && !ns.GetBoundary(this))
                            {
                                string s = Rename(n.ID, n.Parent, allitems);  //map
                                for (i = 0; i < N.r; ++i)   //match with st.mat's row name
                                    if (N.rowNames[i] == s)
                                        break;
                                if (i < N.r)   //index valid
                                {
                                    N[i, j] += (double)e.ProductsHash[ns];
                                }
                            }
                        }
                    }

                    ++j;
                }
                /*
                foreach (string s in species.Keys)
                {
                    Species n = species[s] as Species;

                    j = 0;
                    if (n != null && !n.Boundary)
                    {
                        N.rowNames[i] = s;
                        foreach (string r in reactions.Keys)
                        {
                            N.columnNames[j] = r;

                            Edge e = reactions[r] as Edge;
                            if (e != null && n != null)
                            {
                                double inflow = 0, outflow = 0;
                                if (e.ProductsHash.ContainsKey(n))
                                {
                                    inflow += (double)e.ProductsHash[n];
                                }
                                if (e.ReactantsHash.ContainsKey(n))
                                {
                                    outflow += (double)e.ReactantsHash[n];
                                }
                                N[i, j] = inflow - outflow;
                            }

                            ++j;
                        }
                        ++i;
                    }
                }*/
                return N;
            }
        }

        /// <summary>
        /// Gets all the mappings that are contained in this module.
        /// The modules in which the items reside may be modules inside this module
        /// </summary>
        public virtual Hashtable AllMappings
        {
            get
            {
                List<Item> modules = AllItemsOfType(GetType());
                Hashtable mappings = new Hashtable();
                foreach (Item i in modules)   //for all the modules inside
                {
                    Module module = i as Module;
                    if (module != null)
                    {
                        Hashtable map = module._MapTo;
                        if (map != null && map.Count > 0)
                        {
                            foreach (Item i2 in map.Keys)  //for each of its mappings i2->t
                            {
                                Item t = map[i2] as Item;
                                if (t != null && i2.SharedModule(t) == this)  //fetch it if this is the shared module
                                {
                                    mappings[i2] = t;
                                }
                            }
                        }
                    }
                }
                return mappings;
            }
        }

        /// <summary>
        /// write the mappings to xml...
        /// </summary>
        /// <param name="writer"></param>
        protected virtual void WriteXMLMappings(XmlWriter writer, Hashtable mappings, bool flip)
        {
            Module root = Root as Module;
            if (root == null) return;

            Hashtable allitems = root.AllItemsWithPrefix;

            if (mappings.Count > 0)
            {
                foreach (Item i1 in mappings.Keys)
                {
                    Item i2 = mappings[i1] as Item;
                    string s1 = root.Rename(i1.ID, i1.Parent, allitems, false),
                           s2 = root.Rename(i2.ID, i2.Parent, allitems, false);

                    if (flip)
                    {
                        writer.WriteStartElement("From", "");
                        writer.WriteValue(s2);
                        writer.WriteEndElement();

                        writer.WriteStartElement("To", "");
                        writer.WriteValue(s1);
                        writer.WriteEndElement();
                    }
                    else
                    {
                        writer.WriteStartElement("From", "");
                        writer.WriteValue(s1);
                        writer.WriteEndElement();

                        writer.WriteStartElement("To", "");
                        writer.WriteValue(s2);
                        writer.WriteEndElement();
                    }
                }
            }
        }

        protected virtual void ReadXMLMappings(XmlReader reader)
        {
            Module root = Root as Module;

            Hashtable allitems = null;
            if (root != null) allitems = root.AllItemsWithPrefix;

            bool read = true;

            if (!(reader.NodeType == XmlNodeType.Element && reader.Name == "Mappings"))
                read = reader.Read();

            if (!(reader.NodeType == XmlNodeType.Element && reader.Name == "Mappings")) return;

            read = reader.Read();

            string from = "";

            while (read)
            {
                if (reader.NodeType == XmlNodeType.EndElement)
                    break;

                switch (reader.Name)
                {
                    case "From":
                        read = reader.Read();
                        from = reader.ReadContentAsString();
                        break;
                    case "To":
                        read = reader.Read();
                        string to = reader.ReadContentAsString();
                        if (!string.IsNullOrEmpty(from) && !string.IsNullOrEmpty(to) && root != null)
                        {
                            Item i1 = root.FindItem(from, allitems), i2 = root.FindItem(to, allitems);
                            if (i1 != null && i1.ParentModule != null)
                            {
                                if (i2 != null)
                                    i1.ParentModule.SetMapping(i1, i2);  //phew!
                                else
                                    i1.ParentModule.SetMapping(i1, i1); //expose
                            }
                        }
                        from = "";
                        break;
                    default:
                        while (reader.NodeType != XmlNodeType.EndElement)
                            read = reader.Read();
                        break;
                }
                if (!read) break;
                read = reader.Read();
            }
        }

        /// <summary>
        /// writes each item in this module into xml as well as module parameters
        /// </summary>
        /// <param name="writer"></param>
        public override void WriteXML(XmlWriter writer)
        {
            base.WriteXML(writer);

            if (_Parameters.Count > 0 || _MapFrom.Count > 0 || _MapTo.Count > 0)
            {
                writer.WriteStartElement("Module", "");

                if (_Parameters.Count > 0)
                {
                    writer.WriteStartElement("Parameters", "");

                    foreach (String name in _Parameters.Keys)
                    {
                        writer.WriteStartElement("Parameter", "");

                        writer.WriteStartElement("Name", "");
                        writer.WriteString(name);
                        writer.WriteEndElement();

                        writer.WriteStartElement("Value", "");
                        writer.WriteValue(GetParameter(name));
                        writer.WriteEndElement();

                        writer.WriteEndElement();
                    }

                    writer.WriteEndElement(); //end parameters
                }
                if (_MapTo.Count > 0 || _MapFrom.Count > 0)
                {
                    writer.WriteStartElement("Mappings", "");
                    WriteXMLMappings(writer, _MapTo, false);
                    WriteXMLMappings(writer, _MapFrom, true);
                    writer.WriteEndElement();
                }
                if (_Rules.Count > 0)
                {
                    writer.WriteStartElement("AlgebraicRules", "");

                    foreach (String name in _Rules.Keys)
                    {
                        writer.WriteStartElement("AlgebraicRules", "");

                        writer.WriteStartElement("Name", "");
                        writer.WriteString(name);
                        writer.WriteEndElement();

                        writer.WriteStartElement("Value", "");
                        writer.WriteValue(_Rules[name]);
                        writer.WriteEndElement();

                        writer.WriteEndElement();
                    }

                    writer.WriteEndElement(); //end rules
                }
                writer.WriteEndElement(); //end module
            }
        }

        /// <summary>
        /// Reads the parameters section of the xml document
        /// </summary>
        /// <param name="reader"></param>
        void ReadXMLParameters(XmlReader reader)
        {
            if (!(reader.NodeType == XmlNodeType.Element && reader.Name == "Parameter"))
                reader.Read();

            while (reader.NodeType == XmlNodeType.Element && reader.Name == "Parameter")
            {
                string name = "";
                double value = 1d;
                reader.Read();
                while (reader.NodeType == XmlNodeType.Element)
                {
                    switch (reader.Name)
                    {
                        case "Name":
                            reader.Read();
                            if (reader.NodeType == XmlNodeType.Text)
                                name = reader.ReadContentAsString();
                            break;
                        case "Value":
                            reader.Read();
                            if (reader.NodeType == XmlNodeType.Text)
                            {
                                try { value = reader.ReadContentAsDouble(); }
                                catch { value = 1d; }
                            }
                            break;
                        default:
                            reader.Read();
                            break;
                    }
                    reader.Read();
                }
                if (!string.IsNullOrEmpty(name))
                    SetParameter(name, value);
                reader.Read();
            }
        }

        /// <summary>
        /// Reads the rules section of the xml document
        /// </summary>
        /// <param name="reader"></param>
        void ReadXMLRules(XmlReader reader)
        {
            if (_Rules == null) _Rules = new Hashtable();

            if (!(reader.NodeType == XmlNodeType.Element && reader.Name == "AlgebraicRules"))
                reader.Read();

            while (reader.NodeType == XmlNodeType.Element && reader.Name == "AlgebraicRules")
            {
                string name = "";
                string value = "";
                reader.Read();
                while (reader.NodeType == XmlNodeType.Element)
                {
                    switch (reader.Name)
                    {
                        case "Name":
                            reader.Read();
                            if (reader.NodeType == XmlNodeType.Text)
                                name = reader.ReadContentAsString();
                            break;
                        case "Value":
                            reader.Read();
                            if (reader.NodeType == XmlNodeType.Text)
                            {
                                try { value = reader.ReadContentAsString(); }
                                catch { value = ""; }
                            }
                            break;
                        default:
                            reader.Read();
                            break;
                    }
                    reader.Read();
                }
                if (!string.IsNullOrEmpty(name) && !string.IsNullOrEmpty(value))
                    _Rules[name] = value;

                reader.Read();
            }
        }

        /// <summary>
        /// reads all the parameters and each node
        /// </summary>
        /// <param name="reader"></param>
        /// <returns></returns>
        public override bool ReadXML(XmlReader reader)
        {
            bool read = base.ReadXML(reader);
            
            if (!(reader.NodeType == XmlNodeType.Element && reader.Name == "Module"))
                read = reader.Read();
            
            if (reader.NodeType == XmlNodeType.Element && reader.Name == "Module")
            {
                read = reader.Read();
                Hashtable parameters = new Hashtable();

                while (read)
                {
                    if (reader.NodeType == XmlNodeType.EndElement)
                        break;

                    if (reader.NodeType == XmlNodeType.Element)
                    {
                        switch (reader.Name)
                        {
                            case "Parameters":
                                ReadXMLParameters(reader);
                                parameters = _Parameters;   //save the parameters, because adding edges will change them
                                _Parameters = new Hashtable();
                                break;
                            case "Mappings":
                                ReadXMLMappings(reader);
                                break;
                            default:
                                while (reader.NodeType != XmlNodeType.EndElement)
                                    read = reader.Read();
                                break;
                        }
                    }
                    if (!read || (reader.NodeType == XmlNodeType.EndElement && reader.Name == "Module")) break;
                    if (reader.NodeType != XmlNodeType.Element)
                        read = reader.Read();
                }

                if (parameters != null)
                {
                    if (_Parameters == null) _Parameters = new Hashtable();
                    foreach (string key in parameters.Keys)
                    {
                        _Parameters[key] = parameters[key];
                    }
                }
                parameters = null;
                return true;
            }
            return false;
        }
        /*
        /// <summary>
        /// Returns the item that has the given ID
        /// (recursively searches upwards)
        /// </summary>
        /// <param name="ID">ID</param>
        /// <returns>Item</returns>
        public override Item FindItem(string ID, bool searchOutside)
        {
            if (ID.Equals(Model.Waste.ID)) return Module.Waste;
            if (ID.Equals(Model.Source.ID)) return Module.Source;

            if (_Items == null) _Items = new Hashtable();

            Hashtable items = base.AllItems;

            foreach (string id in items.Keys)
            {
                Item item = items[id] as Item;
                if (item != null)
                {
                    if (ID.Equals(id))
                        return item;
                }
            }

            if (searchOutside)
            {
                if (Parent is Container)
                    return ((Container)Parent).FindItem(ID);
            }

            return null;
        }*/
    }
}
